﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;
using System.Timers;
using IRCBot.Messages;
using IRCBot.Modules;


namespace IRCBot
{
    public class Client
    {
        private TcpClient _connection = null;
        private ClientConfiguration _config;
        private NetworkStream _networkStream = null;
        private StreamReader _streamReader = null;
        private StreamWriter _streamWriter = null;
        private bool _quit = false;
        
        private List<OutgoingMessage> _delayedMessages = new List<OutgoingMessage>();
        private Timer _sendDelayedMessageTimer;
        private List<IModule> _modules = new List<IModule>();
        private string _channel;

        public Client(ClientConfiguration config, string channel)
        {
            _channel = channel;
            _config = config;
            InitializeModules();

            EstablishConnection();

            try
            {
                InitializeNetworkStreams();
                SetUserInfo();
                sendData(new OutgoingMessage(CommandType.JOIN, channel));
                _sendDelayedMessageTimer = new Timer(1000);
                _sendDelayedMessageTimer.Elapsed += new ElapsedEventHandler(sendDelayedMessageTimer_Elapsed);
                _sendDelayedMessageTimer.AutoReset = true;
                _sendDelayedMessageTimer.Start();
                IRCWork();
            }
            catch(Exception ex)
            {
                Console.WriteLine("Communication error - " + ex.Message);
            }
            finally
            {
                Disconnect();
            }
        }

        private void InitializeModules()
        {
            _modules.Add(new GeneralModule());
            _modules.Add(new DrawingModule());
            _modules.Add(new PinterestModule());

            foreach (IModule module in _modules)
            {
                module.Say += new SayEvent(Say);
                module.SendData += new SendDataEvent(sendData);
                module.DelayedSay += new DelayedSayEvent(DelayedSay);
                module.ClearDelayedMessages += new ModuleEvent(module_ClearDelayedMessages);
            }
        }

        void module_ClearDelayedMessages()
        {
            _delayedMessages.Clear();
        }

        void sendDelayedMessageTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            SendDelayedMessages();
        }

        private void Disconnect()
        {
            if (_streamReader != null)
                _streamReader.Close();
            if (_streamWriter != null)
                _streamWriter.Close();
            if (_networkStream != null)
                _networkStream.Close();
            if (_connection != null)
                _connection.Close();
        }

        private void SetUserInfo()
        {
            if (_connection.Connected)
            {
                sendData(new OutgoingMessage(CommandType.USER, _config.Nick, "sketchdaily.net", "sketchdaily.net", _config.Name)); // FIX - Figure out what the 2 strings are for and move those to config
                sendData(new OutgoingMessage(CommandType.NICK, _config.Nick));
            }
            else
                Console.WriteLine("Not connected.");
        }

        private void InitializeNetworkStreams()
        {
            try
            {
                _networkStream = _connection.GetStream();
                _streamReader = new StreamReader(_networkStream);
                _streamWriter = new StreamWriter(_networkStream);                
            }
            catch(Exception ex)
            {
                Console.WriteLine("Error initializing network streams: " + ex.Message);
            }
        }

        private void EstablishConnection()
        {
            try
            {
                _connection = new TcpClient(_config.Server, _config.Port);
            }
            catch(Exception ex)
            {
                Console.WriteLine("Error establishing connection: " + ex.Message);
            }
        }

        public void sendData(OutgoingMessage message)
        {
            string outgoingData = message.Command.ToString();
             
            foreach (string parameter in message.Parameters)
                outgoingData += " " + parameter;

            _streamWriter.WriteLine(outgoingData);
            _streamWriter.Flush();
            Console.WriteLine(outgoingData);
        }

        public void IRCWork()
        {
            string data;
            while (_quit == false)
            {
                data = _streamReader.ReadLine();

                if (data.Contains(":Welcome to the Snoonet Internet Relay Chat Network"))
                {
                    Say("identify password", "nickserv");
                    sendData(new OutgoingMessage(CommandType.JOIN, _channel)); // this is dumb and shouldn't be here.
                }
                Console.WriteLine(data);

                IncomingMessage message = new IncomingMessage(data);

                if (message.Command == "PING")
                    sendData(new OutgoingMessage(CommandType.PONG, message.Parameters));

                HandleData(message);
            }
        }        

        private void Say(string message, string channel)
        {
            try
            {
                sendData(new OutgoingMessage(CommandType.PRIVMSG, channel, ":" + message));
            }
            catch (Exception ex)
            {
                throw new Exception("Say: " + message.ToString());
            }
        }

        private void DelayedSay(string message, string channel, DateTime whenToSend)
        {
            try
            {
                
                _delayedMessages.Add(new OutgoingMessage(CommandType.PRIVMSG, channel, ":" + message) { WhenToSend = whenToSend } );
            }
            catch (Exception ex)
            {
                throw new Exception("DelayedSay: " + message.ToString());
            }
        }

        private void SendDelayedMessages()
        {
            DateTime currentTime = DateTime.Now;

            for(int i = _delayedMessages.Count - 1; i >= 0; i--)
                if (currentTime >= _delayedMessages[i].WhenToSend)
                {
                    sendData(_delayedMessages[i]);
                    _delayedMessages.RemoveAt(i);
                }
        }

        private void HandleQuit(IncomingMessage message)
        {
            sendData(new OutgoingMessage(CommandType.QUIT, "Happy drawing!"));
        }
        
        public void HandleData(IncomingMessage message)
        {
            Debug.Write("Incoming message: ");
            Debug.WriteLine(message.ToString());

            if (message.Sender.ToLower().Contains("panda") == false)
            {
                switch (message.Command.ToLower())
                {
                    //case ":!quit":
                    //    HandleQuit(message);
                    //    _quit = true;
                    //    break;  
                    case ":!pandahard":
                        //Say("▄▀▄▀▄▀▄▀  lıllı ((((|̲̅̅●̲̅̅|̲̅̅=̲̅̅|̲̅̅●̲̅̅|)))) ıllı ♫♫♫♫♫♫♫♫ ~~~~~o", message.Channel);
                        Say("No. Panda has been bad.", message.Channel);
                        break;
                    case ":!partyhard":
                        Say("♪ ♪ ♪ WUB WUB WUB ♪ ♪ ♪", message.Channel);
                        break;
                }

                foreach (IModule module in _modules)
                    module.HandleIncomingMessage(message);
            }

        }



    }
}
